package cn.demoncat.util.mq.util;

import cn.demoncat.util.exception.ReflexRuntimeException;
import cn.demoncat.util.lang.ArrayUtil;
import cn.demoncat.util.lang.JsonUtil;
import cn.demoncat.util.mq.constant.MqLogMsg;
import cn.demoncat.util.mq.entity.MqLog;
import cn.demoncat.util.mq.entity.MqMessage;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.function.Function;

/**
 * MQ消费监听AOP工具
 *
 * 1、打印接收日志、成功日志、失败日志
 * 2、接收者的类必须声明LOG属性
 *      2.1 public static final Logger LOG = LoggerFactory.getLogger(Test.class);
 *      2.2 @Slf4j
 * 3、子类（Kafka、RabbitMQ）编写@Aspect来监听消费者方法，调用around()
 *
 * @author 延晓磊
 * @since 2021年02月17日
 */
public class MqReceiveAspectUtil {

    private static final ReflexRuntimeException NONE_LOG_ERROR = new ReflexRuntimeException("目标类中没有声明 static Logger LOG 属性或注解 @Slf4j");

    /**
     * MQ消费者监听
     *
     * @param joinPoint
     * @param topicSupplier     获取主题
     *
     * @author 延晓磊
     *
     * @since 2018年8月12日
     */
    public static void around(ProceedingJoinPoint joinPoint, Function<Method, String> topicSupplier) {
        // 获取Logger
        Logger log;
        try {
            // 反射获取静态属性（本类中声明，包含私有）
            Field field = ArrayUtil.get(joinPoint.getTarget().getClass().getDeclaredFields(), f -> f.getName().equalsIgnoreCase("LOG"));
            if (field == null) {
                throw NONE_LOG_ERROR;
            }
            field.setAccessible(true);
            log = (Logger)(field.get(null));
        } catch (Exception e) {
            throw NONE_LOG_ERROR;
        }
        // 获取方法参数
        String content = joinPoint.getArgs()[0].toString();
        // 获取方法签名对象
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        // 获取方法对象
        Method method = signature.getMethod();
        // 获取监听主题
        String topic = topicSupplier.apply(method);
        // 接收MQ消息
        MqMessage m;
        try {
            m = JsonUtil.parse(content, MqMessage.class);
        } catch (Exception e) {
            // 收到无效的消息
            log.warn(JsonUtil.toStr(new MqLog(MqLogMsg.RECEIVE_ILLEGAL, topic, content)));
            return;
        }
        // 接收消息成功
        log.info(JsonUtil.toStr(new MqLog(MqLogMsg.RECEIVE, topic, m)));
        try {
            // 执行目标方法，使用原参数
            joinPoint.proceed();
            // 处理消息成功
            log.info(JsonUtil.toStr(new MqLog(MqLogMsg.RECEIVE_SUCCESS, topic, m)));
        } catch (Throwable e) {
            // 处理消息失败：参数校验失败、业务处理失败等
            log.error(JsonUtil.toStr(new MqLog(MqLogMsg.RECEIVE_ERROR, topic, m)), e);
        }
    }

}
